#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <sys/select.h>
#include "Sock.hpp"
#include "Err.hpp"
#include "Log.hpp"

namespace Yohifo
{
    // 多路转接服务器
    static const uint16_t gport = 8888;

    // 第三方数组，维护 fd
    typedef int type_t;
    static const int N = (sizeof(fd_set) * 8);

    static const int defaultFd = -1;

    class SelectServer
    {
    public:
        SelectServer(const uint16_t &port = gport)
            : port_(port)
        {
            logMessage(Debug, "port: %d", port_);
        }

                ~SelectServer()
        {
            listen_sock_.Close();
        }

        void InitServer()
        {
            // 创建套接字、绑定、监听
            listen_sock_.Socket();
            listen_sock_.Bind(port_);
            listen_sock_.Listen();

            fdarray_[0] = listen_sock_.getFd(); // 这个是固定的
            for (int i = 1; i < N; i++)
                fdarray_[i] = defaultFd;

            logMessage(Debug, "socket、bind、listen Success");
        }

        void HandlerEvent(fd_set &rfds)
        {
            for (int i = 0; i < N; i++)
            {
                if(fdarray_[i] == defaultFd)
                    continue;

                if (fdarray_[i] == listen_sock_.getFd() && FD_ISSET(listen_sock_.getFd(), &rfds))
                {
                    // listen 事件已响应，此时 accept 是不会被阻塞的
                    std::string clientIP;
                    uint16_t clientPort;
                    int sock = listen_sock_.Accept(&clientIP, &clientPort);
                    if (sock < 0)
                        return;

                    // 将收到的 sock 存入 fdarray 中
                    int pos;
                    for (pos = 1; pos < N; pos++)
                        if (fdarray_[pos] == defaultFd)
                            break;
                    if (pos == N)
                    {
                        // fdarray 满了
                        close(sock);
                        logMessage(Warning, "Fdarray Full: [%d/%d]", pos, N);
                    }
                    else
                    {
                        // 存入 fdarray
                        fdarray_[pos] = sock;
                        DebugPrint();
                    }
                }
                else if(fdarray_[i] != listen_sock_.getFd() && FD_ISSET(fdarray_[i], &rfds))
                {
                    //已经响应的 fd，此时可以直接读取，并且保证此时读取是一定不会被阻塞的
                    char buff[N];

                    int n = recv(fdarray_[i], buff, sizeof(buff) - 1, 0);
                    if(n > 0)
                    {
                        buff[n - 1] = 0;    //去掉 换行符
                        std::cout << "client #: " << buff << std::endl;

                        //将进行数据回响
                        std::string msg = buff;
                        msg = "[server echo]: " + msg + "\n";

                        send(fdarray_[i], msg.c_str(), msg.size(), 0);
                    }
                    else
                    {
                        if(n == 0)
                        {
                            //读取到末尾而结束
                            logMessage(Debug, "Recv end");
                        }
                        else
                        {
                            //读取失败
                            logMessage(Warning, "Recv fail");
                        }

                        //关闭文件描述符、清理 fdarray
                        int fd = fdarray_[i];
                        close(fdarray_[i]);
                        fdarray_[i] = defaultFd;
                        DebugPrint();

                        logMessage(Debug, "Close %d Success", fd);
                    }
                }
            }
        }

        void StartServer()
        {
            // 在获取链接之前，需要将 fd 保存至 fd_set 中
            while (true)
            {
                // 更新 rfds
                fd_set rfds;
                FD_ZERO(&rfds);

                int maxFd = 0;
                for (int i = 0; i < N; i++)
                {
                    if (fdarray_[i] != defaultFd)
                    {
                        FD_SET(fdarray_[i], &rfds);
                        maxFd = max(maxFd, fdarray_[i]);
                    }
                }

                // struct timeval timeout = {2, 0}; //阻塞时间 2 秒
                int n = select(maxFd + 1, &rfds, nullptr, nullptr, nullptr);
                switch (n)
                {
                case 0:
                    logMessage(Debug, "timeout, %d %s", errno, strerror(errno));
                    break;
                case -1:
                    logMessage(Warning, "%d %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(Info, "有一个事件就绪了 %d", n);
                    HandlerEvent(rfds);
                    break;
                }
            }
        }

        void DebugPrint()
        {
            //打印 fdarray 信息
            std::cout << "==================================" << std::endl;
            std::cout << "fdarray: ";
            for(int i = 0; i < N; i++)
                if(fdarray_[i] != defaultFd)
                    std::cout << fdarray_[i] << " ";
            std::cout << std::endl << "==================================" << std::endl;
        }

    private:
        Sock listen_sock_;
        uint16_t port_;
        type_t fdarray_[N];
    };
}